#include <stdint.h>
#include <stdlib.h>
#include "CFG.h"
#include "../List.h"
#include "TU.h"

static __LinkedList* VisitStack;
static __LinkedList* Cycle;

static void InitSCC(CFGNode* Node){
    uint64_t SCCIndex;
    CFGEdge* Edge;
    
    SCCIndex = Node->SCC_Index;
    
    Node->_Visited = 1;
    
    Node->CYC_Color = DFSC_WHITE;
    
    LST_Rewind(Node->Edges);    
    
    while((Edge = LST_Next(Node->Edges))){
        if(Edge->Count){
            if(Edge->Target->SCC_Index == SCCIndex){
                if(!Edge->Target->_Visited){
                    InitSCC(Edge->Target);
                }
            }
        }
    }
    
}

static void Visit(CFGNode* Node, CFGNode* InitialNode){
    uint32_t NumEdges, i;
    CFGEdge *GCE, *Edge;       //greatest count edge
    CFGNode *TargetNode, *CycleNode;
    uint64_t Greatest;
    
    if(Cycle)
        return;
    
    Node->CYC_Color = DFSC_GRAY;
    LST_Push(VisitStack, Node);
    
    NumEdges = 0;
    
    LST_Rewind(Node->Edges);
    
    while((Edge = LST_Next(Node->Edges)))
        if(Edge->Count)
            NumEdges++;
    
    for(i = 0; i < NumEdges; i++){
        Greatest = 0;
        
        //Select next greatest edge count that points 
        //to a node within the same SCC
        LST_Rewind(Node->Edges);
        while((Edge = LST_Next(Node->Edges))){
            if(Edge->Target->SCC_Index == InitialNode->SCC_Index){
                if(Edge->Count > Greatest)
                    Greatest = Edge->Count, GCE = Edge;
            }
        }
        
        TargetNode = GCE->Target;
        
        if(TargetNode->CYC_Color == DFSC_GRAY){
            Cycle = LST_New(NULL);
            while((CycleNode = LST_Pop(VisitStack))){
                if(CycleNode != TargetNode){
                    LST_Push(Cycle, CycleNode);
                }
                else
                    break;
            }
            LST_Push(Cycle, TargetNode);
        }
        else if(TargetNode->CYC_Color == DFSC_WHITE){
            Visit(TargetNode, InitialNode);
        }
        
        if(Cycle)
            break;
    }
    
    LST_Pop(VisitStack);
    
    Node->CYC_Color = DFSC_BLACK;
}

/*
 
 * visitar(v, h)
 * 
 * v.color = gray
 * visit_stack.push(v)
 * for each edge (v, u) in transition descending order
 *      if u.mark == gray
 *              ciclo.insert(stack.pop) until top == h
 *              //encerrar visita.
 *      else if u.mark == white
 *              visitar(u, v)
 *      end if
 * end for
 * v.mark = black
 * 
 
 */

__LinkedList* CYCLE_GetCycle(CFGNode* InitialNode){
    InitSCC(InitialNode);
    VisitStack = LST_New(NULL);
    Cycle = NULL;
    Visit(InitialNode, InitialNode);
    
    LST_Destroy(VisitStack);
    
    return Cycle;
}

static uint64_t temp(){
    //Temperature = How many instructions per node * executions (dynamic size)
    
}

uint64_t CYCLE_GetTemperature(__LinkedList* Cycle){
    CFGNode *Node, *PrevNode, *FirstNode;
    CFGEdge *Edge;
    
    uint64_t Temp = 0;
    
    LST_Rewind(Cycle);
    
    PrevNode = FirstNode = LST_Next(Cycle);
    
    while((Node = LST_Next(Cycle))){
        Edge = CFGN_EdgeTo(PrevNode, TU_GetStartingAddress(Node->Unit));
        Temp += Edge->Count * Node->EpochExecutionCount;
        PrevNode = Node;
    }
    
    Edge = CFGN_EdgeTo(PrevNode, TU_GetStartingAddress(FirstNode->Unit));
    Temp += Edge->Count * FirstNode->EpochExecutionCount;
    
    return Temp;
    
}
